﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Shapes;
using System.Windows.Threading;
using SynergyControls;
using ScheduledXAudio2;
using System.Threading;
//using XAudio2Controls.Sequencer;

namespace XAudio2Controls
{
    public class XA2Sequencer : CustomControlBase
    {
        #region Delegates

        //public delegate void BarSubmitDelegate(int i);
        public delegate void BarSubmitDelegate();
        public delegate void BarChanger();


        #endregion Delegates
        #region Events
        public static event RoutedEventHandler TotalBarsIncreasedEvent;
        public static event RoutedEventHandler TotalBarsDecreasedEvent;
        public static event RoutedEventHandler BarSelectedEvent;
        #endregion Events

        #region Singleton Pattern


        private static XA2Sequencer instance;
        public static XA2Sequencer Instance
        {
            get
            {
                if (instance == null)
                    instance = new XA2Sequencer();
                return instance;
            }
        }

        #endregion Singleton Pattern

        #region fields
        public static ObservableCollection<ObservableCollection<RhythmButtonBar>> MasterBarCollection = new ObservableCollection<ObservableCollection<RhythmButtonBar>>();
        public static ObservableCollection<XA2TabPanelBase> ClientControls = new ObservableCollection<XA2TabPanelBase>();
        private ObservableCollection<Control> BarRepresenters;
        #endregion fields

        #region Properties
        private Thread SequencerThread
        { get; set; }
        public static int CurrentBarIndex
        {
            get;
            protected set;
        }
        public Canvas MainDisplay
        {
            get { return ResourceRoot.FindName("PART_MainDisplay") as Canvas; }
        }
        protected Button BtnAddBar
        {
            get { return ResourceRoot.FindName("PART_BtnAddBar") as Button; }
        }
        protected Button BtnRemoveBar
        {
            get { return ResourceRoot.FindName("PART_BtnRemoveBar") as Button; }
        }
        protected Label TotalBarsLbl
        {
            get { return ResourceRoot.FindName("PART_TotalBarsLbl") as Label; }
        }

        #endregion Properties

        #region Dependency Properties



        #region SelectedBar

        private static readonly DependencyProperty SelectedBarProperty =
            DependencyProperty.Register("SelectedBar", typeof(Object), typeof(XA2Sequencer), new PropertyMetadata(new PropertyChangedCallback(SelectedBarChanged)));

        internal static void SelectedBarChanged(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            XA2Sequencer instance = dependency as XA2Sequencer;
            if (instance != null)
            {
                instance.SelectedBarChanged(args.NewValue, args.OldValue);
            }
        }
        internal virtual void SelectedBarChanged(Object newvalue, Object oldvalue)
        {
            if (BarRepresenters.Contains((Object)newvalue))
            {
                //System.Windows.MessageBox.Show(BarRepresenters.IndexOf((Shape)newvalue).ToString());
                AnyBar_Click(newvalue, new EventArgs());
            }
        }
        public Object SelectedBar
        {
            get { return GetValue(SelectedBarProperty); }
            set
            {
                SetValue(SelectedBarProperty, value);
            }
        }
        private void ChangeBar()
        {
            SelectedBar = instance.BarRepresenters[CurrentBarIndex];
        }
        #endregion SelectedBar

        #region TotalBars
        private static readonly DependencyProperty TotalBarsProperty =
            DependencyProperty.Register("TotalBars", typeof(int), typeof(XA2Sequencer), new PropertyMetadata(new PropertyChangedCallback(TotalBarsChanged)));

        internal static void TotalBarsChanged(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            XA2Sequencer instance = dependency as XA2Sequencer;
            if (instance != null)
            {
                instance.TotalBarsLbl.Content = "Total Bars: " + instance.TotalBars.ToString();
                instance.TotalBarsChanged((int)args.NewValue, (int)args.OldValue);
            }
        }
        internal virtual void TotalBarsChanged(int newvalue, int oldvalue)
        {
            if (newvalue > oldvalue)
            {
                if (TotalBarsIncreasedEvent != null)
                {
                    TotalBarsIncreasedEvent(instance, new RoutedEventArgs());
                }
                foreach (DrumPatchTabPanel tpb in ClientControls)
                {

                    if (tpb.IsVisible)
                    {
                        if (tpb.TabPatternPanel != null)
                            tpb.RefreshTabPatternPanel(true);
                        return;
                    }

                }
            }

            if (newvalue < oldvalue)
            {
                if (TotalBarsDecreasedEvent != null)
                {
                    TotalBarsDecreasedEvent(instance, new RoutedEventArgs());
                }
                foreach (DrumPatchTabPanel tpb in ClientControls)
                {
                    if (tpb.TabPatternPanel != null)
                        tpb.TabPatternPanelRemoveBar();
                }
            }


        }

        public int TotalBars
        {
            get { return (int)GetValue(TotalBarsProperty); }
            set { SetValue(TotalBarsProperty, value); }
        }
        #endregion TotalBars

        #endregion Dependency Properties

        static XA2Sequencer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(XA2Sequencer), new FrameworkPropertyMetadata(typeof(XA2Sequencer)));
            TotalBarsIncreasedEvent += XA2Sequencer_TotalBarsIncreasedEvent;
            TotalBarsDecreasedEvent += XA2Sequencer_TotalBarsDecreasedEvent;
        }
        private XA2Sequencer()
        {
            CurrentBarIndex = 0;
            BarRepresenters = new ObservableCollection<Control>();
            BarRepresenters.CollectionChanged += BarRepresenters_CollectionChanged;
            BarSelectedEvent += XA2Sequencer_BarSelectedEvent;
            ScheduledXAudio2.Engine.OnOffNotifier.PropertyChanged += new PropertyChangedEventHandler(OnOffNotifier_PropertyChanged);
        }


        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            GetTemplateChildren();
        }

        #region Methods

        internal void SubmitNextBarToClock()
        {
            if (BarRepresenters.Count >= 1)
            {
                AnyBar_Click((BarRepresenters[CurrentBarIndex] as Bar), new EventArgs(), false);
                foreach (ObservableCollection<RhythmButtonBar> b in MasterBarCollection)
                {
                    ObservableCollection<RhythmButtonBar> Bars;
                    Bars = b;//{acknowledgement of redundancy}
                    if (Engine.IsRunning && Bars != null
                        && CurrentBarIndex >= 0
                        && CurrentBarIndex < BarRepresenters.Count
                        && !(Bars[CurrentBarIndex].Owner as XA2TabPanelBase).IsTerminal)
                    {
                        for (int i = 0; i < Bars[CurrentBarIndex].Pattern.Length; i++)
                        {
                            if (Bars[CurrentBarIndex].Pattern[i] == 1
                                && Bars[CurrentBarIndex].Owner == ClientControls[MasterBarCollection.IndexOf(Bars)])
                            {
                                if (!Engine.IsReceiving)
                                    return;

                                Clock.Instance.Schedule(
                                    new DrumMessage(
                                        (DrumPatchHost)ClientControls[MasterBarCollection.IndexOf(Bars)].SoundEngine,
                                        Channel.Channel10,
                                        ClientControls[MasterBarCollection.IndexOf(Bars)].SoundEngine.UNIT,
                                        Clock.Instance.Time + (i / 4f)));
                            }
                        }
                    }
                    else
                        return;
                }
                SubmitCallbackToClock(BarRepresenters.Count);
            }
        }
        private void SubmitCallbackToClock(int _totalbars)
        {
            if (!Engine.IsReceiving)
                return;
            try
            {
                if (_totalbars - 1 >= CurrentBarIndex + 1)
                {
                    ++CurrentBarIndex;
                }
                else
                {
                    CurrentBarIndex = 0;
                }
                BarSubmitDelegate bsd = new BarSubmitDelegate(SubmitNextBarToClock);
                try
                {
                    CallbackMessage b = new CallbackMessage(bsd, CurrentBarIndex, ScheduledXAudio2.Engine.Scheduler.Time + ((MasterBarCollection[0])[0].Pattern.Length) / 4f);
                    ScheduledXAudio2.Engine.Scheduler.Schedule(b);
                }
                catch (NullReferenceException)
                { }
            }
            catch (ArgumentOutOfRangeException)
            {
                System.Windows.MessageBox.Show("ArgumentOutOFRangeException while calling SubmitCallbackToClock!");
                return;
            }
        }
        private void ConfigureButtons()
        {
            if (BtnAddBar != null)
            {
                BtnAddBar.Click += BtnAddBar_Click;
                BtnRemoveBar.Click += BtnRemoveBar_Click;
            }
        }
        internal static void RegisterBarCollection(XA2TabPanelBase collectionHolder)
        {
            if (!MasterBarCollection.Contains(collectionHolder.Bars))
            {
                MasterBarCollection.Add(collectionHolder.Bars);
                ClientControls.Add(collectionHolder);
                while (collectionHolder.Bars.Count < instance.TotalBars)
                {
                    collectionHolder.AddNewRhythmButtonBar();
                }
            }
            else
                System.Windows.MessageBox.Show("MasterBarCollection already has this Bar collection! Something is not working correctly!");
        }
        internal static RhythmButtonBar RegisterRhythmButtonBar(XA2TabPanelBase registerer)
        {
            return new RhythmButtonBar((registerer.Bar_ID_Dispenser_Property), (registerer.Phrase_ID_Dispenser_Property), registerer);
        }
        internal override void GetTemplateChildren()
        {


            ConfigureButtons();
        }
        #endregion Methods
        #region Event Handlers
        static void OnOffNotifier_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (ScheduledXAudio2.Engine.IsRunning == false)
            {
                CurrentBarIndex = 0;
                return;
            }
            BarSubmitDelegate bars = new BarSubmitDelegate(instance.SubmitNextBarToClock);
            bars.Invoke();
        }
        void BtnAddBar_Click(object sender, RoutedEventArgs e)
        {
            ++TotalBars;
        }
        void BtnRemoveBar_Click(object sender, RoutedEventArgs e)
        {
            if (TotalBars >= 1)
                --TotalBars;
        }
        static void XA2Sequencer_TotalBarsDecreasedEvent(object sender, RoutedEventArgs e)
        {
            if (instance.BarRepresenters.Count > 0)
                instance.BarRepresenters.Remove(instance.BarRepresenters[instance.BarRepresenters.Count - 1]);
        }
        static void XA2Sequencer_TotalBarsIncreasedEvent(object sender, RoutedEventArgs e)
        {
            //System.Windows.MessageBox.Show("static bars increased event was raised!");
            if (instance.BarRepresenters == null)
            {
                instance.BarRepresenters = new ObservableCollection<Control>();
            }
            Bar bar = new Bar();
            bar.MouseDown += instance.AnyBar_Click;
            Instance.BarRepresenters.Add(bar);
        }
        void BarRepresenters_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (instance.BarRepresenters != null)
            {
                instance.MainDisplay.Children.Clear();
                foreach (int s in Enumerable.Range(0, BarRepresenters.Count))
                {
                    var bar = BarRepresenters[s] as Bar;
                    if (bar != null)
                    {
                        instance.MainDisplay.Children.Add(bar);
                        bar.Margin = new Thickness((40 * s) + (6 * s), MainDisplay.ActualHeight / 2, 0, 0);
                    }
                }
            }
        }

        private void AnyBar_Click(object sender, EventArgs e)
        {
            var bar = sender as Bar;
            AnyBar_Click(bar, e, true);
        }
        internal virtual void AnyBar_Click(object sender, EventArgs e, bool TriggeredByHuman = true)
        {
            var bar = sender as Bar;
            bar.Select();
            if (TriggeredByHuman)
            {
                CurrentBarIndex = BarRepresenters.IndexOf(bar);
            }
        }

        void XA2Sequencer_BarSelectedEvent(object sender, RoutedEventArgs e)
        {
            SelectedBar = sender as Object;
        }
        #endregion Event Handlers

    }
}
